package org.lc.gateway.filter;

import cn.hutool.core.util.ObjUtil;
import lombok.AllArgsConstructor;
import org.lc.gateway.context.GatewayContext;
import org.lc.gateway.enums.FilterOrderEnum;
import org.lc.gateway.properties.GatewayPluginProperties;
import org.lc.gateway.util.SchemeUtil;
import org.lc.gateway.util.WebfluxResponseUtils;
import org.lc.gateway.util.XssInjectionRuleUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Objects;

/**
 * 防xss注入
 *
 * @author lc
 */

@AllArgsConstructor
public class XssInjectionFilter implements GlobalFilter, Ordered {

    private GatewayPluginProperties gatewayPluginProperties;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        GatewayContext gatewayContext = exchange.getAttribute(GatewayContext.CACHE_GATEWAY_CONTEXT);
        /* 当不是TCP 请求 或者没有请求数据的时候直接放行  */
        var isNoRequestData = !Objects.requireNonNull(gatewayContext).getReadRequestData();
        if (SchemeUtil.isNoHttpAndHttps(request.getURI().getScheme()) || isNoRequestData) {
            return chain.filter(exchange);
        }

        /* 当返回参数为true时，记录请求参数和返回参数 */
        if (gatewayPluginProperties.shouldXssInjection(exchange)) {
            /* 此处判断query 里面是否含有xss关键语句 */
            MultiValueMap<String, String> queryParams = request.getQueryParams();
            if (XssInjectionRuleUtils.mapRequestXssKeyWordsCheck(queryParams)) {
                return WebfluxResponseUtils.responseWrite(exchange, "参数中不允许存在XSS注入关键字");
            }
            HttpHeaders headers = request.getHeaders();
            MediaType contentType = headers.getContentType();
            if (headers.getContentLength() > 0 && ObjUtil.isNotNull(contentType)) {
                /* 此处判断body (json 或表单) 里面是否含有Xss关键语句 */
                boolean hasSql = (contentType.includes(MediaType.APPLICATION_JSON) &&
                        XssInjectionRuleUtils.jsonRequestXssKeyWordsCheck(gatewayContext.getRequestBody()))
                        || (contentType.includes(MediaType.APPLICATION_FORM_URLENCODED)
                        && XssInjectionRuleUtils.mapRequestXssKeyWordsCheck(gatewayContext.getFormData()));

                if (hasSql) {
                    return WebfluxResponseUtils.responseWrite(exchange, "参数中不允许存在XSS注入关键字");
                }
            }
        }
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return FilterOrderEnum.REQUEST_XSS_FILTER.getOrder();
    }
}